125 lines · 5.2 KB
| 1 | --- |
| 2 | import Repo from '../../../layouts/Repo.astro'; |
| 3 | import { apiGet } from '../../../lib/api'; |
| 4 | |
| 5 | const { owner, repo } = Astro.params; |
| 6 | const cookie = Astro.request.headers.get('cookie') || ''; |
| 7 | |
| 8 | let repoData: any = null; |
| 9 | let tree: any = null; |
| 10 | let readme: any = null; |
| 11 | let branches: any = null; |
| 12 | let activity: any[] = []; |
| 13 | let error = ''; |
| 14 | |
| 15 | try { |
| 16 | repoData = await apiGet(`/api/repos/${owner}/${repo}`, cookie); |
| 17 | branches = await apiGet(`/api/repos/${owner}/${repo}/branches`, cookie); |
| 18 | tree = await apiGet(`/api/repos/${owner}/${repo}/tree/${repoData.default_branch || 'main'}`, cookie); |
| 19 | |
| 20 | // Try to find and load README |
| 21 | const readmeEntry = tree.entries?.find((e: any) => |
| 22 | /^readme(\.(md|txt|rst))?$/i.test(e.name) && e.type === 'file' |
| 23 | ); |
| 24 | if (readmeEntry) { |
| 25 | readme = await apiGet( |
| 26 | `/api/repos/${owner}/${repo}/blob/${repoData.default_branch || 'main'}/${readmeEntry.name}`, |
| 27 | cookie |
| 28 | ); |
| 29 | } |
| 30 | |
| 31 | activity = await apiGet(`/api/repos/${owner}/${repo}/activity`, cookie); |
| 32 | } catch (e: any) { |
| 33 | error = e.message; |
| 34 | } |
| 35 | |
| 36 | const ref = repoData?.default_branch || 'main'; |
| 37 | const cloneUrl = `${new URL(Astro.request.url).origin}/${owner}/${repo}.git`; |
| 38 | --- |
| 39 | |
| 40 | <Repo owner={owner!} repo={repo!} activeTab="code" ref={ref}> |
| 41 | {error && <div class="flash-error">{error}</div>} |
| 42 | |
| 43 | {repoData && ( |
| 44 | <> |
| 45 | {/* Clone URL */} |
| 46 | <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;"> |
| 47 | <div style="display: flex; align-items: center; gap: 8px;"> |
| 48 | <span style="font-size: 0.875rem; color: var(--text-muted);">Branch:</span> |
| 49 | <span class="btn" style="font-size: 0.8125rem;">{ref}</span> |
| 50 | </div> |
| 51 | <div style="display: flex; align-items: center; gap: 8px;"> |
| 52 | <code style="background: var(--bg-tertiary); padding: 4px 12px; border-radius: var(--radius); border: 1px solid var(--border); font-size: 0.8125rem; color: var(--text-muted);"> |
| 53 | git clone {cloneUrl} |
| 54 | </code> |
| 55 | </div> |
| 56 | </div> |
| 57 | |
| 58 | {/* File listing */} |
| 59 | {tree && ( |
| 60 | <div class="card" style="padding: 0; overflow: hidden;"> |
| 61 | <table style="width: 100%; border-collapse: collapse;"> |
| 62 | <tbody> |
| 63 | {tree.entries.map((entry: any) => ( |
| 64 | <tr style="border-bottom: 1px solid var(--border);"> |
| 65 | <td style="padding: 8px 16px; font-size: 0.875rem;"> |
| 66 | <span style="color: var(--text-muted); margin-right: 8px;"> |
| 67 | {entry.type === 'dir' ? '📁' : '📄'} |
| 68 | </span> |
| 69 | {entry.type === 'dir' ? ( |
| 70 | <a href={`/${owner}/${repo}/tree/${ref}/${entry.name}`}>{entry.name}</a> |
| 71 | ) : ( |
| 72 | <a href={`/${owner}/${repo}/blob/${ref}/${entry.name}`}>{entry.name}</a> |
| 73 | )} |
| 74 | </td> |
| 75 | </tr> |
| 76 | ))} |
| 77 | </tbody> |
| 78 | </table> |
| 79 | </div> |
| 80 | )} |
| 81 | |
| 82 | {/* README */} |
| 83 | {readme && ( |
| 84 | <div class="card" style="margin-top: 16px;"> |
| 85 | <div style="padding: 8px 0; border-bottom: 1px solid var(--border); margin-bottom: 16px;"> |
| 86 | <strong style="font-size: 0.875rem;">{readme.path}</strong> |
| 87 | </div> |
| 88 | <div style="font-size: 0.875rem; line-height: 1.6;"> |
| 89 | <pre style="white-space: pre-wrap; font-family: var(--font-sans);">{readme.content}</pre> |
| 90 | </div> |
| 91 | </div> |
| 92 | )} |
| 93 | |
| 94 | {/* Activity feed */} |
| 95 | {activity.length > 0 && ( |
| 96 | <div style="margin-top: 20px;"> |
| 97 | <h3 style="font-size: 1rem; margin-bottom: 12px; color: var(--text-muted);">Recent activity</h3> |
| 98 | <div class="card" style="padding: 0;"> |
| 99 | {activity.slice(0, 10).map((event: any) => ( |
| 100 | <div style="padding: 8px 16px; border-bottom: 1px solid var(--border); font-size: 0.8125rem; display: flex; justify-content: space-between; align-items: center;"> |
| 101 | <div> |
| 102 | <strong style="color: var(--text);">{event.username || 'unknown'}</strong> |
| 103 | {event.action === 'push' && ( |
| 104 | <span style="color: var(--text-muted);"> pushed to <code style="background: var(--bg-tertiary); padding: 1px 4px; border-radius: 3px;">{event.details?.ref}</code></span> |
| 105 | )} |
| 106 | {event.action === 'create_mr' && ( |
| 107 | <span style="color: var(--text-muted);"> opened MR <a href={`/${owner}/${repo}/merge-requests/${event.details?.mr_number}`}>#{event.details?.mr_number}</a> {event.details?.mr_title}</span> |
| 108 | )} |
| 109 | {event.action === 'merge_mr' && ( |
| 110 | <span style="color: var(--text-muted);"> merged MR <a href={`/${owner}/${repo}/merge-requests/${event.details?.mr_number}`}>#{event.details?.mr_number}</a> {event.details?.mr_title}</span> |
| 111 | )} |
| 112 | {event.action === 'create_repo' && ( |
| 113 | <span style="color: var(--text-muted);"> created this repository</span> |
| 114 | )} |
| 115 | </div> |
| 116 | <span style="color: var(--text-muted); font-size: 0.75rem; white-space: nowrap;">{event.created_at}</span> |
| 117 | </div> |
| 118 | ))} |
| 119 | </div> |
| 120 | </div> |
| 121 | )} |
| 122 | </> |
| 123 | )} |
| 124 | </Repo> |
| 125 |